package com.aaron.demo1.common;

import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 单机redis锁
 * @author liurong
 * @date 2021/7/23 9:34
 */
public class MyRedisLock {

    StringRedisTemplate stringRedisTemplate;

    //互斥--原子性
    //死锁
    //同一个

    //死锁问题

    private String lockName;

    private String lockPassword;

    private boolean ownLock = false;

    //getLock相当于new一个lock,用构造方法来当做getLock
    public MyRedisLock(String key,StringRedisTemplate stringRedisTemplate){
        this.stringRedisTemplate = stringRedisTemplate;
        lockName = key + "_lock";
        lockPassword = UUID.randomUUID().toString().replaceAll( "-", "" ).toUpperCase();
    }

    //守护线程：创建线程生，守护线程生；守护随创建线程，生死相随
    public void lock(int leaseTime, TimeUnit unit) {
//        Boolean absent = stringRedisTemplate.opsForValue().setIfAbsent(lockName, lockPassword, leaseTime, unit);
        //while()循环抢锁阻塞
        boolean absent = false;
        while(!absent){
            absent = stringRedisTemplate.opsForValue().setIfAbsent(lockName, lockPassword, leaseTime, unit);
        }

        ownLock = true;
//        Thread currentThread = Thread.currentThread();
        Thread daemonThread = new Thread(new Runnable(){
            @Override
            public void run() {
                long sleepTimeInMillis = calcDaemonThreadSleepTimeInMillis(leaseTime, unit);
                try {
                    while(true){
                        stringRedisTemplate.expire(lockName, leaseTime, unit);
                        //sleep用Time 或者 ScheduledExecutorService好些
                        Thread.sleep(sleepTimeInMillis);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        daemonThread.setDaemon(true);
        daemonThread.start();
    }

    public void unLock(){
        String password = stringRedisTemplate.opsForValue().get(lockName);
//        if (!ownLock || !StringUtils.equals(password,lockPassword)) {
        if (!StringUtils.equals(password,lockPassword)) {
            //throw Exception就必须要处理
//            throw new RuntimeException("锁不存在/未持有该锁");
        }else{
            stringRedisTemplate.delete(lockName);
            ownLock = false;
        }

    }

    public void tryLock(){

    }

    private long calcDaemonThreadSleepTimeInMillis(int time, TimeUnit unit) {


        long millis = unit.toMillis(time);
        //最少睡1s
        if (millis < 3000) {
            millis = 3000;
        }
        return millis / 3;
    }

    public boolean holdLock(){
        return this.ownLock;
    }


}
